package app

import (
	"encoding/json"
	"operator/pkg/config"
	"operator/pkg/format"
	"operator/pkg/network"
	tcp2 "operator/pkg/network/meshnet/tcp"
	"operator/pkg/tracing"
	"operator/pkg/types/resource"
	"operator/pkg/util"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

type MeshClient struct {
	config    *config.SidecarConfig
	tcpClient *tcp2.NewTcpClient
	resource  *map[resource.ResourceString][]resource.Resource

	inBoundServer network.ServerInterface
	ouboundServer network.ServerInterface

	trace      tracing.Interface
	egressList map[string]struct{}
}

func (mc *MeshClient) updateResource(data []byte) []byte {
	err := json.Unmarshal(data, mc.resource)
	if err != nil {
		format.Errors("cmd/sidecar/app/client.go", 35, err.Error())
		return nil
	}

	return []byte("ok")
}

func (mc *MeshClient) updateEgressIP(data []byte) []byte {
	ips := strings.Split(string(data[1:]), ",")
	format.Println("cmd/sidecar/app/client.go", 44, "get egress:", string(data[0]), string(data[1:]))
	for _, i := range ips {
		if data[0] == 1 {
			mc.egressList[i] = struct{}{}
		} else {
			delete(mc.egressList, i)
		}

	}
	return []byte("ok")
}

func (mc *MeshClient) Run() {
	stopCh := make(chan struct{})

	go mc.tcpClient.Run(stopCh)
	go mc.RunFirst()
	go mc.inBoundServer.Run(stopCh)
	go mc.ouboundServer.Run(stopCh)
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGKILL, syscall.SIGINT)

	format.Println("cmd/sidecar/app/client.go", 66, "app run")

	select {
	case x := <-ch:
		switch x {
		case syscall.SIGINT:
			format.Println("cmd/sidecar/app/client.go", 72, "app stop")
		}
	}
}

func (mc *MeshClient) RunFirst() {
	for {
		if mc.tcpClient.Connected() {
			data, err := mc.tcpClient.List()
			if err != nil {
				format.Errors("cmd/sidecar/app/client.go", 82, err.Error())
				return
			}
			mc.updateResource(data)
			return
		}

		time.Sleep(time.Second)
	}

}

func NewMeshClient(c *config.SidecarConfig) *MeshClient {
	client, err := tcp2.NewClient(util.TcpServerPort)
	if err != nil {
		os.Exit(1)
	}

	trace := tracing.NewJaegerTracing(c)
	egress := map[string]struct{}{}

	m := new(map[resource.ResourceString][]resource.Resource)

	inbound, err := network.NewInBoundServer(c.ContainerPort, trace)
	if err != nil {
		os.Exit(1)
	}
	outbound, err := network.NewOutBoundServer(c.Version, m, &egress)
	if err != nil {
		os.Exit(1)
	}

	mc := &MeshClient{
		config:        c,
		tcpClient:     client,
		resource:      m,
		inBoundServer: inbound,
		ouboundServer: outbound,
		trace:         trace,
		egressList:    egress,
	}

	client.AddFn("update", mc.updateResource)
	client.AddFn("update_egress", mc.updateEgressIP)

	return mc
}
